package AOP.base.SelfProxy;

import AOP.base.ClassMake.CharSequenceJavaFileObject;
import AOP.base.ClassMake.JdkDynamicCompileClassLoader;
import AOP.base.ClassMake.JdkDynamicCompileJavaFileManager;

import javax.tools.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 *自定义代理类生成器，实现切面AOP技术支持
 */
public class ProxySourceMaker {

    private Object proxy;

    static String SOURCE_CODE = "";

    private String packageName;

    private String className;

    /**
     * 编译诊断收集器
     */
    static DiagnosticCollector<JavaFileObject> DIAGNOSTIC_COLLECTOR = new DiagnosticCollector<>();


    ProxySourceMaker(Class<?> handlerClass,Class<?> targetClass){
        String packageName = targetClass.getPackage().getName();
        this.packageName = packageName;
        String className = handlerClass.getSimpleName()+targetClass.getSimpleName();
        this.className = className;
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("package").append(" ").append(packageName).append(";").append("\n");
        stringBuilder.append("import ").append(targetClass.getName()).append(";").append("\n");
        stringBuilder.append("import ").append(handlerClass.getName()).append(";").append("\n");
        stringBuilder.append("public class ").append(className).append(" extends ").append(targetClass.getName()).append("{\n");
        //组合处理器对象
        stringBuilder.append(handlerClass.getSimpleName()).append(" ").append(handlerClass.getSimpleName()).
                append(" = ").append("new ").append(handlerClass.getSimpleName()).append("();\n");

        Method[] methods = targetClass.getDeclaredMethods();

        for(Method method:methods){
            stringBuilder.append("@Override").append("\n");
            int m = method.getModifiers();
            //方法修饰符
            stringBuilder.append(Modifier.toString(m)).append(" ");
            //方法返回类型
            stringBuilder.append(method.getReturnType().getName()).append(" ");
            //方法名称
            stringBuilder.append(method.getName()).append("( ");
            //方法参数
            Class<?>[] params = method.getParameterTypes();
            int i=0;
            if(params.length>0){
                for(Class<?> cl:params){
                    stringBuilder.append(cl.getName()).append(" param").append(i++).append(" ");
                }
            }
            stringBuilder.append(")").append("{").append("\n");
            //调用前置处理
            stringBuilder.append(handlerClass.getSimpleName()).append(".before();\n");
            String result = "";
            if(!method.getReturnType().getName().equals("void")){
                stringBuilder.append(method.getReturnType().getName()).append(" ").append("result;").append("\n");
                stringBuilder.append("result = ");
                result = "result";
            }

            //调用目标方法
            stringBuilder.append("super.").append(method.getName()).append("( ");
            for(int j=0;j<i;j++){
                stringBuilder.append("param").append(j).append(" ");
            }
            stringBuilder.append(");").append("\n");

            //调用后置处理
            stringBuilder.append(handlerClass.getSimpleName()).append(".after();\n");
            stringBuilder.append("return ").append(result).append(";").append("\n");

            stringBuilder.append("}").append("\n");
        }

        stringBuilder.append("}\n");

        SOURCE_CODE = stringBuilder.toString();

    }


    public Object getProxy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 获取编译器实例
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // 设置编译参数 - 指定编译版本为JDK1.6以提高兼容性
        List<String> options = new ArrayList<>();
        options.add("-source");
        options.add("1.6");
        options.add("-target");
        options.add("1.6");
        // 获取标准的Java文件管理器实例
        StandardJavaFileManager manager = compiler.getStandardFileManager(DIAGNOSTIC_COLLECTOR, null, null);
        // 初始化自定义类加载器
        JdkDynamicCompileClassLoader classLoader = new JdkDynamicCompileClassLoader(Thread.currentThread().getContextClassLoader());
        // 初始化自定义Java文件管理器实例
        JdkDynamicCompileJavaFileManager fileManager = new JdkDynamicCompileJavaFileManager(manager, classLoader);
        String packageName = this.packageName;
        String className = this.className;
        String qualifiedName = packageName + "." + className;
        // 构建java文件对象字符序列
        CharSequenceJavaFileObject javaFileObject = new CharSequenceJavaFileObject(className, SOURCE_CODE);
        // 添加Java源文件实例到自定义Java文件管理器实例中
        fileManager.addJavaFileObject(
                StandardLocation.SOURCE_PATH,
                packageName,
                className + CharSequenceJavaFileObject.JAVA_EXTENSION,
                javaFileObject
        );

        // 初始化一个编译任务实例并执行编译任务
        List<JavaFileObject> list = new ArrayList<>();
        list.add(javaFileObject);
        JavaCompiler.CompilationTask compilationTask = compiler.getTask(
                null,
                fileManager,
                DIAGNOSTIC_COLLECTOR,
                options,
                null,
                list
        );
        Boolean result = compilationTask.call();
        //打印编译结果
        System.out.println(String.format("编译[%s]结果:%s", qualifiedName, result));
        //加载类获得Class实例
        Class<?> klass = classLoader.loadClass(qualifiedName);
        //根据class实例实例化对象
        proxy = klass.getDeclaredConstructor().newInstance();

        return proxy;
    }

}
